#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>

using namespace std;

/**
 *  解决大数据量的办法   建立学号和索引  对学号进行排序 然后找到对应的索引
 *
 *  二进制读写文件的优点
 *  1、便于对数据进行随机访问（尤其和索引配合）
 *  2、存取效率高
 *  3、可以对文件进行压缩
 *  4、存储密度大
 *  5、数据文件便于保密
 * @return
 */
typedef struct {
    char name[10];
    int num;
    int age;
    char sex;
} Student;
typedef struct {
    int NO;
    int offset;
} StudentIndex;
student stu[5] = {
        {"Li",    1001, 11, 'M'},
        {"Zhang", 1002, 13, 'F'},
        {"Wang",  1003, 15, 'M'},
        {"Zhao",  1004, 16, 'F'},
        {"Liu",   1005, 13, 'M'}
};

void createIndex(); //建立索引
void writeIndex(StudentIndex *s, int stuNum); //写入索引到文件
void init();

void work();

void done();

void displayByIndex();

void displayStudent(Student s);

fstream dataFile, indexFile; //定义全局对象
int stuNum, maxNum;
StudentIndex *studentIndex;

int main() {
    //转换现有数据格式
    //1.读入ASCII文件
    ifstream iofile("ask.txt", ios::in | ios::out);
    int stuNum = 0; //记录学生的人数
    Student students[100]; //假设文件中的数据小于100个
    while (!iofile.end()) {
        iofile >> students[stuNum].name;
        iofile >> students[stuNum].num;
        iofile >> students[stuNum].age;
        iofile >> students[stuNum].sex;
        stuNum++;
    }
    iofile.close();
    //2.写入到二进制文件
    ofstream outputfile("binary.txt", ios::out | ios::binary);
    outputfile.write((char *) &stuNum, sizeof(stuNum)); //首先把总人数写入到文件中
    for (int i = 0; i < stuNum; ++i) {
        outputfile.write((char *) &students[i], sizeof(students[i]));
    }
    outputfile.close();

    //binary.txt就保存了二进制的数据  再就可以对二进制操作

    //1、读入数据
    int stuNum = 0;
    Student *students;
    //首先读取二进制文件的第一位  表示的总人数
    fstream binaryFile("binary.txt", ios::in | ios::binary);
    //读入人数
    binaryFile.read((char *) &stuNum, sizeof(stuNum));
    students = new Student[stuNum];
    //读入数据
    for (int i = 0; i < stuNum; ++i) {
        iofile.read((char *) &students[i], sizeof(students[i]));
    }
    //2、完成业务  可以增删改查

    //3、保存数据
    binaryFile.seekg(0, ios::beg);
    binaryFile.write((char *) &stuNum, sizeof((stuNum)));//对业务操作后，重新改变总人数
    for (int i = 0; i < stuNum; ++i) {
        binaryFile.write((char *) &students[i], sizeof(Student));
    }
    binaryFile.close();

    return 0;
}

void createIndex() {
    int stuNum;
    StudentIndex *studentIndex;
    Student student; //临时变量
    ifstream inputFile("binarydat.dat", ios::in | ios::binary);
    inputFile.read((char *) &stuNum, sizeof(stuNum)); //读入总人数
    studentIndex = new StudentIndex[stuNum];
    long mOffet;//索引
    for (int i = 0; i < stuNum; ++i) {
        mOffet = inputFile.tellg();
        inputFile.read((char *) &student, sizeof(Student)); //读入到临时变量
        studentIndex[i].offset = mOffet;
        studentIndex[i].NO = student.num;
    }
    inputFile.close();
    //采用冒泡对索引表进行排序
    int len = stuNum;
    int flag = 1;
    while (len > 0 && flag = 1) {
        flag = 0;
        for (int i = 0; i < len - 1; ++i) {
            if (studentIndex[i] > studentIndex[i + 1]) {
                flag = 0;
                StudentIndex t = studentIndex[i];
                studentIndex[i] = studentIndex[i + 1];
                studentIndex[i + 1] = t;
            }
        }
        len--;
    }
    //将索引对象排序完之后 再写入到文件中
    writeIndex(studentIndex, stuNum);
}

void writeIndex(StudentIndex *s, int stuNum) {
    ofstream outputFile("index.txt", ios::out | ios::binary);
    if (!outputFile) {
        cerr << "open cerr" << endl;
        exit(1);
    }
    for (int i = 0; i < stuNum; ++i) {
        outputFile.write((char *) &s[i], sizeof(s[i]));
    }
    outputFile.close();
}

void init() {
    dataFile.open("binary.txt", ios::in | ios::out | ios::binary);
    dataFile.read((char *) &stuNum, sizeof(stuNum));
    maxNum = stuNum * 1.1;
    studentIndex = new StudentIndex[maxNum];
    indexFile.open("binarydata.idx", ios::in | ios::binary);
    for (int i = 0; i < stuNum; ++i) {
        indexFile.read((char *) &studentIndex[i], sizeof(StudentIndex));
    }
    indexFile.close();
}

void work() {

}

void done() {
    dataFile.close();
    writeIndex(studentIndex, stuNum); //索引可能会改变
}

void displayByIndex(int stuNum) {
    Student stu;
    int i;
    long location;
    cout << "共" << stuNum << "名考生：" << endl;
    for(int i = 0;  i< stuNum; ++i) {
        location = studentIndex[i].offset; //控制索引下标
        dataFile.seekg(location, ios::beg);
        dataFile.read((char*)&stu, sizeof(Student));
        cout<<i<<":";
        displayStudent(stu);
    }
}
void displayStudent(Student s) {
    cout<<s.num<<endl;
    cout<<s.name<<endl;

}